from pyjvm.instructions.common import Instruction, BytecodeReader
from pyjvm.runtime.thread import Frame
from pyjvm.utils.jprimitive import *


class DivZeroException(Exception):
    pass


def math_type(type_name, execute):
    return type(type_name, (Instruction,), dict(execute=execute))


def add_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 + val2)
        stack.push(ret)

    return math_type(type_name, execute)


def sub_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 - val2)
        stack.push(ret)

    return math_type(type_name, execute)


def mul_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 * val2)
        stack.push(ret)

    return math_type(type_name, execute)


def div_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 / val2)
        stack.push(ret)

    return math_type(type_name, execute)


def i_div_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        if val2 == 0:
            raise DivZeroException('%s / by zero' % val1)
        ret = j_primitive(val1 // val2)
        stack.push(ret)

    return math_type(type_name, execute)


def and_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 & val2)
        stack.push(ret)

    return math_type(type_name, execute)


def or_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 | val2)
        stack.push(ret)

    return math_type(type_name, execute)


def xor_inst_type(type_name, j_primitive):
    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        ret = j_primitive(val1 ^ val2)
        stack.push(ret)

    return math_type(type_name, execute)


class NEG(Instruction):

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        ret = -stack.pop()
        stack.push(ret)


class REM(Instruction):

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        stack.push(val1 % val2)


IADD = add_inst_type('IADD', j_int)
LADD = add_inst_type('LADD', j_long)
FADD = add_inst_type('FADD', j_float)
DADD = add_inst_type('DADD', j_double)

ISUB = sub_inst_type('ISUB', j_int)
LSUB = sub_inst_type('LSUB', j_long)
FSUB = sub_inst_type('FSUB', j_float)
DSUB = sub_inst_type('DSUB', j_double)

IMUL = mul_inst_type('IMUL', j_int)
LMUL = mul_inst_type('LMUL', j_long)
FMUL = mul_inst_type('FMUL', j_float)
DMUL = mul_inst_type('DMUL', j_double)

IDIV = i_div_inst_type('IDIV', j_int)
LDIV = i_div_inst_type('LDIV', j_long)
FDIV = div_inst_type('FDIV', j_float)
DDIV = div_inst_type('DDIV', j_double)

IAND = and_inst_type('IAND', j_int)
LAND = and_inst_type('LAND', j_long)

IOR = or_inst_type('IOR', j_int)
LOR = or_inst_type('LOR', j_long)

IXOR = xor_inst_type('IXOR', j_int)
LXOR = xor_inst_type('LXOR', j_long)


class INEG(NEG):
    pass


class LNEG(NEG):
    pass


class FNEG(NEG):
    pass


class DNEG(NEG):
    pass


class DREM(REM):
    pass


class FREM(REM):
    pass


class IREM(REM):
    pass


class LREM(REM):
    pass


class IINC(Instruction):
    """
    ++ 自增长命令
    """

    def __init__(self):
        Instruction.__init__(self)
        self.index = None
        self.const_value = None

    def fetch_operands(self, reader: BytecodeReader):
        self.index = reader.int8()
        self.const_value = reader.int8()

    def execute(self, frame: Frame):
        val = frame.local_vars[self.index]
        val += self.const_value
        ret = j_int(val)
        frame.local_vars[self.index] = ret


class ISHL(Instruction):  # 算术左移

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x1f
        ret = j_int(val1 << s)
        stack.push(ret)


class ISHR(Instruction):  # 算术右移

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x1f
        ret = j_int(val1 >> s)
        stack.push(ret)


class IUSHR(ISHR):  # 逻辑右移

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x1f
        ret = j_int(j_u_int(val1) >> s)
        stack.push(ret)


class LSHL(Instruction):

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x3f
        ret = j_long(val1 << s)
        stack.push(ret)


class LSHR(Instruction):

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x3f
        ret = j_long(val1 >> s)
        stack.push(ret)


class LUSHR(LSHR):

    def execute(self, frame: Frame):
        stack = frame.operand_stack
        val2 = stack.pop()
        val1 = stack.pop()
        s = j_u_int(val2) & 0x3f
        ret = j_long(j_u_long(val1) >> s)
        stack.push(ret)


__all__ = ['IADD', 'LADD', 'FADD', 'DADD', 'ISUB', 'LSUB', 'FSUB',
           'DSUB', 'IMUL', 'LMUL', 'FMUL', 'DMUL', 'IDIV', 'LDIV',
           'FDIV', 'DDIV', 'IREM', 'LREM', 'FREM', 'DREM', 'INEG',
           'LNEG', 'FNEG', 'DNEG', 'ISHL', 'LSHL', 'ISHR', 'LSHR',
           'IUSHR', 'LUSHR', 'IAND', 'LAND', 'IOR', 'LOR', 'IXOR',
           'LXOR', 'IINC']
